package com.sdk.es.sdk.entity.common;

import com.sdk.es.sdk.constants.EsConts;
import com.sdk.es.sdk.enums.EsQueryKeyEnum;
import com.sdk.es.sdk.enums.SortEnum;
import com.sdk.es.sdk.utils.BeanUtils;
import com.sdk.es.sdk.utils.EsStrUtils;
import com.sdk.es.sdk.utils.SFunction;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author wst
 * @date 2024/3/1
 * 基础查询参数
 */
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class EsBaseParams<T> implements Serializable {
    private static final long serialVersionUID = -6357513444312839799L;

    /**
     * 动态参数列表
     */
    private List<EsParam> esParamList = new ArrayList<>();
    /**
     * 实体对象,用于反射校验esParamList传入的参数是否合法
     */
    private T entity;
    /**
     * 排序方法 {@link SortEnum}
     * 顺序：asc
     * 倒序：desc
     */
    private String sort;

    /**
     * 排序字段
     */
    private String sortBy;

    /**
     * 分组字段,将按照list顺序进行分组，相关接口限定最多三个字段分组，超过不支持
     */
    private List<String> groupByFieldNameList = new ArrayList<>();
    /**
     * 查询指定索引，优先级最高,格式限定：yyyy-MM
     */
    private String indexYearAndMonth;
    /**
     * 查询指定索引，优先级比indexYearAndMonth高,格式限定：yyyy-MM
     */
    private List<String> indexYearAndMonthList;
    /**
     * 求和字段,优先级高于其它参数
     */
    private String sumFieldName;
    /**
     * 特殊查询，类比sql：and  (( key1 = param1 and key2 = param2 ...) or ( key1 = param3 and key2 = param4 ...) or ...)
     * 适用or条件下有and查询的结构
     * 每一个Map<String,String>表示一个or查询
     */
    private List<Map<String,String>> andOrAndList;
    /**
     * 泛型初始化
     *
     * @param entity
     */
    public EsBaseParams(T entity) {
        this.entity = entity;
    }
    /**
     * 禁用无参构造
     */
    private EsBaseParams() {
    }
    /**
     * 无需入参的查询
     */
    public <T> void add(SFunction<T> function, EsQueryKeyEnum esQueryKeyEnum) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), esQueryKeyEnum.getKey()));
    }

    /**
     * 字段不为null
     *
     * @param function
     * @param <T>
     */
    public <T> void filterExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_EXISTS.getKey()));
    }

    public <T> void isNotNull(SFunction<T> function) {
        filterExists(function);
    }

    /**
     * 字段为null
     *
     * @param function
     * @param <T>
     */
    public <T> void mustNotExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.MUST_NOT_EXISTS.getKey()));
    }

    public <T> void isNull(SFunction<T> function) {
        mustNotExists(function);
    }

    /**
     * 或者字段不为null
     *
     * @param function
     * @param <T>
     */
    public <T> void shouldExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.SHOULD_EXISTS.getKey()));
    }

    public <T> void orIsNotNull(SFunction<T> function) {
        shouldExists(function);
    }

    /**
     * 或者字段为null
     *
     * @param function
     * @param <T>
     */
    public <T> void shouldMustNotExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.SHOULD_MUST_NOT_EXISTS.getKey()));
    }

    public <T> void orIsNull(SFunction<T> function) {
        shouldMustNotExists(function);
    }

    /**
     * 在一个and范围内，拼接的or查询，例如：param1 and ( ? or key is not null)
     *
     * @param function
     * @param <T>
     */
    public <T> void filterShouldExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_SHOULD_EXISTS.getKey()));
    }

    public <T> void andOrIsNotNull(SFunction<T> function) {
        filterShouldExists(function);
    }

    /**
     * 在一个and范围内，拼接的or查询，例如：param1 and ( ? or key is null)
     *
     * @param function
     * @param <T>
     */
    public <T> void filterShouldMustNotExists(SFunction<T> function) {
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_SHOULD_MUST_NOT_EXISTS.getKey()));
    }

    public <T> void andOrIsNull(SFunction<T> function) {
        filterShouldMustNotExists(function);
    }

    /**
     * 单参数值查询
     */
    public <T> void add(SFunction<T> function, EsQueryKeyEnum esQueryKeyEnum, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), esQueryKeyEnum.getKey(), objToStr(object)));
    }

    /**
     * 匹配查询，支持空串入参查询
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterTerm(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_TERM.getKey(), objToStr(object)));
    }

    /**
     * 匹配查询，不支持空串入参查询
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void eq(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        filterTerm(function, object);
    }

    /**
     * like查询，支持空串入参查询
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterWildcard(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_WILDCARD.getKey(), objToStr(object)));
    }

    /**
     * like查询，不支持空串入参查询
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void like(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        filterWildcard(function, object);
    }

    /**
     * 不等于，支持空串入参查询
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void mustNotTerm(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.MUST_NOT_TERM.getKey(), objToStr(object)));
    }

    /**
     * 不等于，不支持空串入参查询
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void ne(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        mustNotTerm(function, object);
    }

    /**
     * or key = param
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void shouldTerm(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.SHOULD_TERM.getKey(), objToStr(object)));
    }

    public <T> void orEq(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        shouldTerm(function, object);
    }

    /**
     * or key like '%param%'
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void shouldWildcard(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.SHOULD_WILDCARD.getKey(), objToStr(object)));
    }

    public <T> void orLike(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        shouldWildcard(function, object);
    }

    /**
     * and ( ? or key = param)
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterShouldTerm(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_SHOULD_TERM.getKey(), objToStr(object)));
    }

    public <T> void andOrEq(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        filterShouldTerm(function, object);
    }

    /**
     * and ( ? or key like '%param%')
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterShouldWildcard(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_SHOULD_WILDCARD.getKey(), objToStr(object)));
    }

    public <T> void andOrLike(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        filterShouldWildcard(function, object);
    }

    /**
     * and key like 'param%'
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterPrefix(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if (param == null) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_PREFIX.getKey(), objToStr(object)));
    }

    public <T> void likeRight(SFunction<T> function, Object object) {
        if (object instanceof String) {
            if (object == null || EsStrUtils.isBlank((String) object)) {
                return;
            }
        }
        filterPrefix(function, object);
    }

    /**
     * 范围查询
     */
    public <T> void filterRange(SFunction<T> function, Object startObj, Object endObj) {
        String start = objToStr(startObj);
        String end = objToStr(endObj);
        if (start == null && end == null) {
            return;
        }
        if (start == null) {
            start = EsQueryKeyEnum.FILTER_RANGE.getKey();
        }
        if (end == null) {
            end = EsQueryKeyEnum.FILTER_RANGE.getKey();
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_RANGE.getKey(), start, end));
    }

    public <T> void lt(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if(param == null){
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_LT.getKey(), param));
    }

    public <T> void gt(SFunction<T> function, Object object) {
        String param = objToStr(object);
        if(param == null){
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_GT.getKey(), param));
    }
    /**
     * 列表入参查询
     */
    public <T> void list(SFunction<T> function, EsQueryKeyEnum esQueryKeyEnum, Object object) {
        List<String> paramList = new ArrayList<>();
        List<Object> objectList = (List<Object>) object;
        for (Object obj : objectList) {
            String param = objToStr(obj);
            if (param != null) {
                paramList.add(param);
            }
        }
        if (paramList.size() == 0) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), esQueryKeyEnum.getKey(), paramList));
    }

    /**
     * and key in (param1,param2...)
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterTerms(SFunction<T> function, Object object) {
        List<String> paramList = new ArrayList<>();
        List<Object> objectList = (List<Object>) object;
        for (Object obj : objectList) {
            String param = objToStr(obj);
            if (param != null) {
                paramList.add(param);
            }
        }
        if (paramList.size() == 0) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_TERMS.getKey(), paramList));
    }

    public <T> void in(SFunction<T> function, Object object) {
        filterTerms(function, object);
    }

    /**
     * and key not in (param1,param2...)
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void mustNotTerms(SFunction<T> function, Object object) {
        List<String> paramList = new ArrayList<>();
        List<Object> objectList = (List<Object>) object;
        for (Object obj : objectList) {
            String param = objToStr(obj);
            if (param != null) {
                paramList.add(param);
            }
        }
        if (paramList.size() == 0) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.MUST_NOT_TERMS.getKey(), paramList));
    }

    public <T> void notIn(SFunction<T> function, Object object) {
        mustNotTerms(function, object);
    }

    /**
     * or key in (param,param...)
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void shouldTerms(SFunction<T> function, Object object) {
        List<String> paramList = new ArrayList<>();
        List<Object> objectList = (List<Object>) object;
        for (Object obj : objectList) {
            String param = objToStr(obj);
            if (param != null) {
                paramList.add(param);
            }
        }
        if (paramList.size() == 0) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.SHOULD_TERMS.getKey(), paramList));
    }

    public <T> void orIn(SFunction<T> function, Object object) {
        shouldTerms(function, object);
    }

    /**
     * and ( ? or key in (param,param...))
     *
     * @param function
     * @param object
     * @param <T>
     */
    public <T> void filterShouldTerms(SFunction<T> function, Object object) {
        List<String> paramList = new ArrayList<>();
        List<Object> objectList = (List<Object>) object;
        for (Object obj : objectList) {
            String param = objToStr(obj);
            if (param != null) {
                paramList.add(param);
            }
        }
        if (paramList.size() == 0) {
            return;
        }
        this.getEsParamList().add(EsParam.of(BeanUtils.getFieldName(function), EsQueryKeyEnum.FILTER_SHOULD_TERMS.getKey(), paramList));
    }

    public <T> void andOrIn(SFunction<T> function, Object object) {
        filterShouldTerms(function, object);
    }

    /**
     * 转成字符串
     *
     * @param object
     * @return
     */
    private static String objToStr(Object object) {
        if (object == null) {
            return null;
        }
        if (object instanceof Integer || object instanceof Long || object instanceof Short) {
            return String.valueOf(object);
        } else if (object instanceof String) {
            return (String) object;
        } else if (object instanceof LocalDateTime) {
            return DateTimeFormatter.ofPattern(EsConts.UTC_SIMPLE_PATTERN).format((LocalDateTime) object);
        } else {
            return null;
        }
    }

    public <T> void sortBy(SFunction<T> function, SortEnum sortEnum) {
        this.sortBy = BeanUtils.getFieldName(function);
        this.sort = sortEnum.getKey();
    }

    public <T> void groupBy(SFunction<T> function) {
        String fieldName = BeanUtils.getFieldName(function);
        this.getGroupByFieldNameList().add(fieldName);
    }

    public <T> void sum(SFunction<T> function) {
        this.sumFieldName = BeanUtils.getFieldName(function);
    }
}
